package com.quick.proxy.server.handler.visitor;

import cn.hutool.crypto.digest.MD5;
import com.quick.proxy.core.ChannelConstants;
import com.quick.proxy.core.ProxyChannel;
import com.quick.proxy.core.VisitorChannel;
import com.quick.proxy.core.utils.ProxyUtils;
import com.quick.proxy.core.utils.ResponseUtils;
import com.quick.proxy.server.ProxyResources;
import com.quick.proxy.server.ServerChannelAttribute;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;


/**
 * Description: 访问者代理服务器连接处理程序
 * Author: 吃素不吃肉
 * Date: 2023/7/4
 *
 * @author 吃素不吃肉
 */
@Slf4j
@Sharable
@Component
public class VisitorProxyServerConnectHandler extends ChannelInboundHandlerAdapter {
    /**
     * 获取访问者id
     *
     * @param channel 访问者通道
     * @return 访问者id
     */
    public static String getVisitorId(Channel channel) {
        SocketAddress socketAddress = channel.remoteAddress();
        return MD5.create().digestHex16(socketAddress.toString());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //获取本地端口
        int localPort = ProxyUtils.getLocalPort(ctx.channel());
        //获取访问者id
        String visitorId = getVisitorId(ctx.channel());
        //缓存访问者id
        ctx.channel().attr(ServerChannelAttribute.VISITOR_ID).set(visitorId);
        ProxyChannel pc = new ProxyChannel();
        //获取代理通道
        Channel proxyChannel = ChannelConstants.MAPPING_PORT_PROXY_CHANNEL.get(localPort);
        if (proxyChannel == null) {
            log.info("代理通道不存在");
            ctx.channel().close();
            return;
        }
        //获取真实代理客户端地址
        String realProxyClientAddress = ChannelConstants.MAPPING_PORT_REAL_PROXY_CLIENT_IP_PORT.get(localPort);
        pc.setVisitorId(visitorId);
        pc.setProxyChannel(proxyChannel);
        pc.setRealProxyClientAddress(realProxyClientAddress);
        synchronized (this) {
            //访问者通道绑定代理通道
            ctx.channel().attr(ServerChannelAttribute.PROXY_CHANNEL).set(pc);
            //代理通道绑定访问者通道
            Map<String, VisitorChannel> visitorIdChannel = Optional.ofNullable(proxyChannel.attr(ServerChannelAttribute.VISITOR_ID_CHANNEL).get()).orElse(new HashMap<>(1));
            visitorIdChannel.put(visitorId, new VisitorChannel(visitorId, ctx.channel()));
            proxyChannel.attr(ServerChannelAttribute.VISITOR_ID_CHANNEL).set(visitorIdChannel);
        }
        //通知代理客户端连接真实代理客户端
        ResponseUtils.connect(visitorId, realProxyClientAddress, proxyChannel);
        log.info("visitorId[{}]访问者连接成功", visitorId);
        //通道关闭监听
        ctx.channel().closeFuture().addListener((ChannelFuture channelFuture) -> {
            //清理资源
            ProxyResources.clearVisitorResources(channelFuture.channel());
        });
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isOpen()) {
            ctx.channel().close();
        }
        super.channelInactive(ctx);
    }
}
